// Thud - a dynamic object-oriented language
// Copyright (C) 2009,2010 David H. Hovemeyer <david.hovemeyer@gmail.com>

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

/*
 * Compiler.h
 *
 *  Created on: Nov 24, 2009
 *      Author: dhovemey
 */

#ifndef COMPILER_H_
#define COMPILER_H_

#include <cstdint>
#include <map>
#include <vector>
#include "fe/PreorderASTNodeVisitor.h"
class Class;
class MethodAssembler;
class CodeBase;
class Location;

class LocalVariableScope {
private:
	typedef std::map<std::wstring, int8_t> LocalVarMap;

	LocalVarMap m_localVarMap;
	int8_t m_nextLocal;

	LocalVariableScope(const LocalVariableScope &);
	LocalVariableScope& operator=(const LocalVariableScope &);

public:
	LocalVariableScope(int8_t start);
	~LocalVariableScope();

	/**
	 * Add a local variable (which could be a parameter or a regular local variable)
	 * to this scope.
	 *
	 * @param varName name of the variable
	 * @param varLoc  source location of variable declaration
	 * @return local variable number of the variable in the compiled bytecode
	 */
	int8_t addLocalVar(const std::wstring &varName, const Location &varLoc);

	/**
	 * Get the local variable number of a local variable
	 * in this scope.
	 *
	 * @param varName name of the variable
	 * @return local variable number of the variable in the compiled bytecode
	 */
	int8_t getLocalVar(const std::wstring &varName) const;

	/**
	 * Return true if given variable is defined as a local variable
	 * in this scope, false otherwise.
	 *
	 * @param varName name of local variable
	 */
	bool localVarDefined(const std::wstring &varName) const;

	unsigned getMaxLocalVars() const;

};

/**
 * A ClassAndMethodContext stores all of the needed context
 * information about the class and (if any) method currently being compiled.
 * A stack is used because top-level statements are considered to
 * be part of the special Main$ class and main$ method.
 */
class ClassAndMethodContext {
private:
	//typedef std::map<std::wstring, int8_t> LocalVarMap;
	typedef std::vector<LocalVariableScope *> ScopeStack;

	Class *m_cls;
	ASTNode *m_clsASTNode;
	MethodAssembler *m_methodAsm;
	//LocalVarMap m_localVarMap;
	ScopeStack m_scopeStack;
	unsigned m_numLocals;

	bool m_endOfParameters;

	ClassAndMethodContext(const ClassAndMethodContext &);
	ClassAndMethodContext& operator=(const ClassAndMethodContext &);

public:
	/**
	 * Constructor.
	 * @param clsName name of class
	 * @param vm      CodeBase containing all Classes
	 */
	ClassAndMethodContext(const std::wstring &clsName, CodeBase *vm, ASTNode *clsASTNode);

	/** Destructor. */
	~ClassAndMethodContext();

	/**
	 * Get the Class object representing the class being compiled.
	 */
	Class *getClass() { return m_cls; }

	/**
	 * Get the MethodAssembler assembling the current method.
	 */
	MethodAssembler *getMethodAssembler() { return m_methodAsm; }

	/**
	 * Enter a method.
	 *
	 * @param methodName name of the method
	 * @param flags      the method flags
	 * @param methodLoc  the source location of the method
	 */
	void enterMethod(const std::wstring &methodName, uint32_t flags, const Location &methodLoc);

	/**
	 * Leave current method.
	 */
	void leaveMethod();

	/**
	 * Enter a new local variable scope.
	 */
	void enterScope();

	/**
	 * Leave current local variable scope.
	 */
	void leaveScope();

	/**
	 * Called to indicate that all of the variable declarations
	 * representing method parameters for the current method
	 * have been seen.
	 */
	void endOfParameters();

	/**
	 * Add a local variable (which could be a parameter or a regular local variable).
	 *
	 * @param varName name of the variable
	 * @param varLoc  source location of variable declaration
	 * @return local variable number of the variable in the compiled bytecode
	 */
	int8_t addLocalVar(const std::wstring &varName, const Location &varLoc);

	/**
	 * Get the local variable number of a local variable.
	 *
	 * @param varName name of the variable
	 * @param refLoc  source location of variable reference
	 * @return local variable number of the variable in the compiled bytecode
	 */
	int8_t getLocalVar(const std::wstring &varName, const Location &refLoc) const;

	/**
	 * Return true if given variable is defined as a local variable, false otherwise.
	 *
	 * @param varName name of local variable
	 */
	bool localVarDefined(const std::wstring &varName) const;

	/**
	 * Return true if current class contains given field.
	 *
	 * @param fieldName name of field
	 * @return true if current class contains the field, false otherwise
	 */
	bool currentClassContainsField(const std::wstring &fieldName) const;

private:
	int lookupLocalVar(const std::wstring &varName) const;
};

/**
 * Compiler is an AST node visitor which creates compiled
 * bytecode classes/methods in the CodeBase.
 */
class Compiler : public PreorderASTNodeVisitor {
public:
	typedef PreorderASTNodeVisitor Base;

private:
	typedef std::map<std::wstring, std::wstring> ImportMap;
	typedef std::vector<ClassAndMethodContext *> ContextStack;

	CodeBase *m_vm;
	ImportMap m_importMap;
	ImportMap m_staticImportMap;
	ContextStack m_contextStack;

public:
	/**
	 * Constructor.
	 *
	 * @param vm the CodeBase which stores compiled classes
	 */
    Compiler(CodeBase *vm);

    /** Destructor. */
    virtual ~Compiler();

    /**
     * Compile given source file, depositing resulting class(es) in given code base.
     *
     * @param fileName file name of a source file to compile
     * @param codeBase code base in which to deposit class(es) resulting from compilation of source
     */
    static void compileSource(const std::string &fileName, CodeBase *codeBase);

    /**
     * Compile source file whose AST is given.
     *
     * @param parseTree the AST of a source file
     */
    void compile(ASTNode *parseTree);

    virtual void visitStatementList(ASTNode *n);
    virtual void visitStatement(ASTNode *n);
    virtual void visitWhileStatement(ASTNode * n);
    virtual void visitIfStatement(ASTNode * n);
    virtual void visitBinaryOpExpr(ASTNode * n);
    virtual void visitUnaryOpExpr(ASTNode * n);
    virtual void visitFieldRefExpr(ASTNode *n);
    virtual void visitMethodCallExpr(ASTNode *n);
    virtual void visitModuleMethodCallExpr(ASTNode * n);
    virtual void visitVarRefExpr(ASTNode *n);
    virtual void visitIntLiteralExpr(ASTNode *n);
    virtual void visitBooleanLiteralExpr(ASTNode * n);
    virtual void visitSymbolLiteralExpr(ASTNode *n);
    virtual void visitStringLiteralExpr(ASTNode *n);
    virtual void visitSelfRefExpr(ASTNode *n);
    virtual void visitNewExpr(ASTNode * n);
    virtual void visitClass(ASTNode *n);
    virtual void visitModule(ASTNode *n);
    virtual void visitMethod(ASTNode *n);
    virtual void visitMagicMethod(ASTNode *n);
    virtual void visitField(ASTNode *n);
    virtual void visitVar(ASTNode *n);
    virtual void visitImport(ASTNode * n);
    virtual void visitStaticImport(ASTNode *n);
    virtual void visitReturnStatement(ASTNode *n);
	virtual void visitNilExpr(ASTNode * n);

private:
	bool containsExecutableStatements(ASTNode *n);
	void enterClass(const std::wstring &clsName, ASTNode *clsASTNode);
	void leaveClass();
	ClassAndMethodContext *getTopContext();
    void compileClassOrModule(ASTNode *n);
    void compileMethod(ASTNode *n, uint32_t flags);
    void compileMethodCall(ASTNode *argList, const std::wstring &methodName, const Location &loc);
};

#endif /* COMPILER_H_ */
